/*
  自定义兼容到 ie9 的 Promise 函数模块
  我们知道原生 promise 是不支持 ie 的 ie11也不支持。
  而自定义 promise 之所以能够兼容到 ie9 是因为核心异步实现使用了 setTimeOut
  同时这也使得 自定义的 promise 和原生的 promise 在任务队列中有差别
  自定义promise: 宏队列
  原生 promise: 微队列
*/
(function(window){
  // 定义 promise 是三种状态
  var PENDING = 'pending';
  var RESOLVED= 'resolved';
  var REJECTED = 'rejected';
  /*
    Promise 构造函数
    executor 执行性函数（同步执行）
  */
  function Promise(executor) {
    // 保存当前 promise 对象
    var self = this;
    // 给 promise 对象指定三个属性
    self.status = PENDING;
    // 存储结果数据的属性
    self.data = undefined;
    // 每个元素结构 {onResolved(){},onRejected(){}}
    self.callbacks = [];
    // 定义外部调用时执行器中的两个函数
    function resolve(value) {
      if(self.status !== PENDING) {
        return;
      }
      // 状态改为 resolved
      self.status = RESOLVED;
      self.data = value;
      // 如果有待执行 callback 函数，立即异步执行回到函数 onResolved
      if(self.callbacks.length) {
        setTimeout(function() {
          // 执行队列中所有与成功的回调
          self.callbacks.forEach(function(callbackObj) {
            callbackObj.onResolved(value);
          })
        });
      }
    }
    function reject(reason) {
      if(self.status !== PENDING) {
        return;
      }
      // 状态改为 resolved
      self.status = REJECTED;
      self.data = reason;
      if(self.callbacks.length) {
        setTimeout(function() {
          self.callbacks.forEach(function(callbackObj) {
            callbackObj.onRejected(reason);
          })
        });
      }
    }
    // 立即同步执行 executor
    try {
      executor(resolve, reject);
    } catch (error) {
      // 如果执行器抛出异常，promise 对象变为 rejected 状态
      reject(error);
    }
  }
  /*
    Promise 原型对象方法
    接收成功和失败的回调函数
    返回一个新的 promise 对象
  */
  Promise.prototype.then = function(onResolved, onRejected) {
    // 如果 onResolved 不是函数，则给出默认函数向后传递成功的 value
    onResolved = typeof onResolved === 'function'? onResolved: function(vlaue) {return value};
    // 如果 onRejected 不是函数，则指定默认失败的回调（实现错误/异常传递 的关键点） 向后传递失败的 reason
    onRejected = typeof onRejected === 'function'? onRejected: function(reason) {throw reason};

    var self = this;
    return new Promise(function(resolve, reject) {
      /*
        执行指定的回调函数
        根据执行函数返回的结果 改变返回 promise 的状态
      */
      function handle(callback) {
        // 如果抛出异常，返回 promise 的结果为失败
        try {
          var result = callback(self.data);
          // 如果回调函数返回是 promise return 的 promise 结果就是这个函数返回的 promise 结果
          // 如果回调函数返回的不是 promise retrun 的 promise 就会成功，返回结果就是 result
          if(result instanceof Promise) {
            result.then(resolve, reject)
          }else{
            resolve(result)
          }
        } catch (error) {
          reject(error)
        }
      }
      if(self.status === PENDING) {
        // PENDING 等待状态
        // 将回调函数保存起来 并改变返回的 promise 的状态
        self.callbacks.push({
          onResolved: function(){
            handle(onResolved)
          },
          onRejected: function() {
            handle(onRejected)
          }
        });
      }else if(self.status === RESOLVED) {
        // RESOLVED 成功状态
        // 异步执行 onResolved 并改变返回的 promise 的状态
        setTimeout(function() {
          handle(onResolved);
        });
      }else {
        // REJECTED 拒绝状态
        // 异步执行 onRejected 并改变返回的 promise 的状态
        setTimeout(function() {
          handle(onRejected);
        });
      }
    })
  }

  /*
    接收一个失败的回调函数
    返回一个新的 promise 对象
  */
  Promise.prototype.catch = function(onRejected) {
    return this.then(undefined, onRejected);
  }

  // Promise 函数对象方法
  /*
    返回一个指定结果成功的promise返回
  */
  Promise.resolve = function(value) {
    return new Promise(function(resolve, reject){
      // value 是 promise 使用 value 的结果为 promise 的结果
      // vlaue 不是 promise promise 变为成功，数据是 value
      if(value instanceof Promise) {
        value.then(resolve, reject)
      }else{
        resolve(value)
      }
    })
  }
  /*
    返回一个指定 reason 失败的 promise
  */
  Promise.reject = function(reason) {
    return new Promise(function(resolve, reject){
      reject(reason)
    })
  }
  /*
    参数接收一个 promise 数组
    返回一个 promise 只有当所有 promise 都成功的时候返回成功的回调
    有一个失败就返回失败的回调
  */
  Promise.all = function(promises) {
    // 用来保存所有成功的 promise 值的数组
    var values = [];
    // 用来保存成功 promise 的数量
    var count = 0;
    return new Promise(function(resolve, reject){
      // 遍历所有 promise 获取每个 promise 的结果
      promises.forEach(function(p, index){
        // 这里如果不是 promise 则转成 promise
        if(!(p instanceof Promise)) {
          p = Promise.resolve(p);
        }
        p.then(function(value){
          values[index] = value;
          count++;
          if(count === promises.length) {
            resolve(values);
          }
        }, reject)
      })
    })
  }
  /*
    参数接收一个 promise 数组
    返回一个promise 其结果由第一个完成的 promise 决定
  */
  Promise.race = function(promises) {
    return new Promise(function(resolve, reject) {
      promises.forEach(function(p) {
        // 这里如果不是 promise 则转成 promise
        if(!(p instanceof Promise)) {
          p = Promise.resolve(p);
        }
        p.then(resolve, reject)
      })
    })
  }
  // 向外部暴露 Promise 函数
  window.Promise = Promise;
})(window);